package org.rainbowinverted.charging.device.connect.ykc.server.handler;

import cn.hutool.core.util.HexUtil;
import org.rainbowinverted.charging.device.connect.ykc.server.constants.ChannelAttributeKeyConstants;
import org.rainbowinverted.charging.device.connect.ykc.server.message.in.YkcMessageIn;
import org.rainbowinverted.charging.device.connect.ykc.server.message.out.YkcMessageOut;
import org.rainbowinverted.charging.device.connect.ykc.server.util.Cp56Time2aUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 云快充抽象处理器
 * @author hehuaiping
 */
@Slf4j
public abstract class YkcAbstractHandler<IN, OUT extends YkcMessageOut> {
    /**
     * CHANNEL_CONTEXT_MAP
     * 存放设备和channel的对应关系
     */
    protected static final Map<Long, ChannelHandlerContext> CHANNEL_CONTEXT_MAP = new ConcurrentHashMap<>(16);
    /**
     * FUTURE_MAP
     */
    protected final Map<Integer, CompletableFuture<IN>> FUTURE_MAP = new ConcurrentHashMap<>(16);

    /**
     * 发送消息
     * @param deviceNo 设备编号
     * @param message 消息
     * @return {@link IN}
     */
    public IN sendMsg(Long deviceNo, OUT message) throws Exception {
        ChannelHandlerContext channel = getChannel(deviceNo);
        if (Objects.isNull(channel)) {
            log.error("deviceNo:{} channel is null", deviceNo);
            return null;
        }
        ChannelFuture channelFuture = channel.writeAndFlush(message);
        try {
            // 等待IO操作完成
            channelFuture.get();
            // 获取设备反馈结果
            CompletableFuture<IN> future = FUTURE_MAP.get(message.getFrameSerialNo());
            if (Objects.nonNull(future)) {
                return future.get(15, TimeUnit.SECONDS);
            }
        }finally {
            // 移除future
            FUTURE_MAP.remove(message.getFrameSerialNo());
        }
        return null;
    }

    /**
     * 根据deviceNo获取channel
     *
     * @param deviceNo deviceNo
     * @return {@link ChannelHandlerContext}
     */
    protected ChannelHandlerContext getChannel(Long deviceNo) {
        return CHANNEL_CONTEXT_MAP.get(deviceNo);
    }

    /**
     * put channel
     *
     * @param deviceNo              设备编号
     * @param channelHandlerContext channel
     */
    protected void putChannel(Long deviceNo, ChannelHandlerContext channelHandlerContext) {
        if (Objects.isNull(deviceNo)) {
            return;
        }
        channelHandlerContext.channel().attr(ChannelAttributeKeyConstants.CHANNEL_ATTR_DEVICE_NO).set(deviceNo);
        ChannelHandlerContext oldChannel = CHANNEL_CONTEXT_MAP.putIfAbsent(deviceNo, channelHandlerContext);
        if (Objects.isNull(oldChannel) || !oldChannel.equals(channelHandlerContext)) {
            CHANNEL_CONTEXT_MAP.put(deviceNo, channelHandlerContext);
        }
    }

    /**
     * 移除
     *
     * @param deviceNo 设备编号
     */
    protected void removeChannel(Long deviceNo) {
        if (Objects.isNull(deviceNo)) {
            return;
        }
        ChannelHandlerContext handlerContext = CHANNEL_CONTEXT_MAP.remove(deviceNo);
        handlerContext.disconnect();
        handlerContext.close();
    }

    /**
     * 移除
     *
     * @param channel channel
     */
    protected void removeChannel(ChannelHandlerContext channel) {
        if (Objects.isNull(channel)) {
            return;
        }
        ChannelHandlerContext handlerContext = CHANNEL_CONTEXT_MAP.remove(getDeiceNo(channel));
        handlerContext.disconnect();
        handlerContext.close();
        channel.disconnect();
        channel.close();
    }

    /**
     * createCompletableFuture
     * @param frameSerialNo frameSerialNo
     */
    protected void createCompletableFuture(Integer frameSerialNo) {
        FUTURE_MAP.put(frameSerialNo, new CompletableFuture<>());
    }

    /**
     * complete
     * @param frameSerialNo frameSerialNo
     * @param in in
     */
    protected void complete(Integer frameSerialNo, IN in) {
        CompletableFuture<IN> future = FUTURE_MAP.get(frameSerialNo);
        if (Objects.nonNull(future)) {
            future.complete(in);
        }
    }

    /**
     * 获取设备编号
     *
     * @param channelContext channel
     * @return {@link String}
     */
    protected Long getDeiceNo(ChannelHandlerContext channelContext) {
        return channelContext.channel().attr(ChannelAttributeKeyConstants.CHANNEL_ATTR_DEVICE_NO).get();
    }

    /**
     * 读取byte转int
     * @param byteBuf byteBuf
     * @return {@link Integer}
     */
    protected Integer readByteToInt(ByteBuf byteBuf) {
        return byteBuf.readByte() & 0xFF;
    }

    /**
     * 写入设备编号
     * @param byteBuf byteBuf
     * @param deviceNo deviceNo
     */
    protected void writeDeviceNo(ByteBuf byteBuf, Long deviceNo) {
        byteBuf.writeBytes(HexUtil.decodeHex(String.format("%07d", deviceNo)));
    }

    /**
     * 写入枪编号
     * @param byteBuf byteBuf
     * @param gunNo gunNo
     */
    protected void writeGunNo(ByteBuf byteBuf, Integer gunNo) {
        byteBuf.writeBytes(HexUtil.decodeHex(String.format("%01d", gunNo)));
    }

    /**
     * 读取deviceNo
     * @param byteBuf byteBuf
     * @return {@link Long}
     */
    protected Long readDeviceNo(ByteBuf byteBuf) {
        // 设备编号
        byte[] deviceNoBytes = new byte[7];
        byteBuf.readBytes(deviceNoBytes);
        return Long.parseLong(HexUtil.encodeHexStr(deviceNoBytes), 10);
    }

    /**
     * 读取gunNo
     * @param byteBuf byteBuf
     * @return {@link Long}
     */
    protected Integer readGunNo(ByteBuf byteBuf) {
        return (int) byteBuf.readByte();
    }

    /**
     * 读取orderNo
     * @param byteBuf byteBuf
     * @return {@link String}
     */
    protected String readOrderNo(ByteBuf byteBuf) {
        byte[] orderNoBytes = new byte[16];
        byteBuf.readBytes(orderNoBytes);
        return HexUtil.encodeHexStr(orderNoBytes);
    }

    /**
     * 写入orderNo
     * @param byteBuf byteBuf
     * @param orderNo orderNo
     */
    protected void writeOrderNo(ByteBuf byteBuf, String orderNo) {
        byteBuf.writeBytes(HexUtil.decodeHex(String.format("%32s", orderNo).replace(' ', '0')));
    }

    protected LocalDateTime readCp56Time2a(ByteBuf byteBuf) {
        byte[] cp56Time2aBytes = new byte[7];
        byteBuf.readBytes(cp56Time2aBytes);
        return Cp56Time2aUtils.hexCp56Time2aToTime(HexUtil.encodeHexStr(cp56Time2aBytes));
    }

    /**
     * 读入
     * @param channel channel
     * @param message message
     */
    public abstract void read(ChannelHandlerContext channel, YkcMessageIn message);

    /**
     * 写入
     *
     * @param byteBuf byteBuf
     * @param out out
     */
    public abstract void write(ByteBuf byteBuf, OUT out);

}
